<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>AdminLTE 3 | Vue3 | Layout</title>

  <!-- Google Font: Source Sans Pro -->
  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback">
  <!-- Font Awesome Icons -->
  <link rel="stylesheet" href="plugins/fontawesome-free/css/all.min.css">
  <!-- Theme style -->
  <link rel="stylesheet" href="css/adminlte.min.css">
</head>
<body>
<div class="wrapper" id="app">

  <!-- 顶部页头 -->
  <lte-main-header>
	  <!-- 这里要写完整的标签，如果写简写的标签，只能渲染出一个组件，原因未知（可能是把第二个当成了结束标签了吧）-->
	  <lte-main-header-navbar-left></lte-main-header-navbar-left>
	  <!-- @sidebar-collapse 监听由控制面板按钮发出来的事件，对Sidebar进行显示或隐藏 -->
	  <lte-main-header-navbar-right @sidebar-collapse="collapseSidebar"></lte-main-header-navbar-right>
  </lte-main-header>

  <!-- 左侧菜单 -->
  <lte-main-sidebar @click.stop style="padding-bottom: 10px;">
	  <lte-main-sidebar-logo title="AdminLTE 3 + Vue3" logo="img/AdminLTELogo.png"></lte-main-sidebar-logo>
	  <lte-sidebar>
		  <lte-user-panel title="Alexander Pierce" logo="img/user2-160x160.jpg"></lte-user-panel>
		  <nav class="mt-2"><lte-treeview :items="menus"></lte-treeview></nav>
	  </lte-sidebar>
  </lte-main-sidebar>

  <!-- 工作区 -->
  <lte-main-content></lte-main-content>
  
  <!-- 系统设置区 -->
  <lte-control-sidebar :style="{display:controlSidebarDisplay}"></lte-control-sidebar>

  <!-- 底部页脚 -->
  <lte-main-footer>
	  <lte-copy-right></lte-copy-right>
	  <lte-version></lte-version>
  </lte-main-footer>
  
  <!-- TODO: 需要考虑这个层的作用 -->
  <div id="sidebar-overlay"></div>
</div>
<!-- ./wrapper -->

<script src="https://unpkg.com/vue@3.0.11/dist/vue.global.js"></script>
<script src="https://unpkg.com/mitt/dist/mitt.umd.js"></script>
<script src="js/FullScreen.js"></script>
<script src="js/PushMenu.js"></script>
<script src="js/Logo.js"></script>
<script src="js/CopyRight.js"></script>
<script src="js/Version.js"></script>
<script src="js/ControlSidebar.js"></script>
<script src="js/MainSidebar.js"></script>


<script type="text/javascript">
	if (!window.emitter) {
		window.emitter = mitt();
	}
	const app = Vue.createApp({
		// 必须是函数形式
		data() {
			return {
				message: "Hello Vue!",
				menus: [
					{
						id: 1,
						title: 'Starter Pages',
						url: '',
						target: 'xxx',
						icon: 'fas fa-tachometer-alt',
						type: 'menu', // label
						isNew: true,
						children: [
							{
								id: 2,
								parentId: 1,
								title: 'Active Page',
								url: '#',
								target: 'xxx',
								icon: 'far fa-circle',
								type: 'menu'
							},{
								id: 3,
								parentId: 1,
								title: 'Inactive Page',
								url: '#',
								target: 'xxx',
								icon: 'far fa-circle',
								type: 'menu'
							}
						]
					},{
						id: 4,
						title: 'Simple Link',
						url: 'https://adminlte.io/',
						target: '_blank',
						icon: 'fas fa-th',
						type: 'menu',
						isNew: true
					},{
						id: 5,
						title: 'Open adminlte.io',
						url: '',
						target: 'xxx',
						icon: 'fas fa-tachometer-alt',
						type: 'menu', // label
						isNew: true,
						children: [
							{
								id: 6,
								parentId: 5,
								title: 'adminlte.io',
								url: 'https://adminlte.io/',
								target: '_blank',
								icon: 'far fa-circle',
								type: 'menu'
							},{
								id: 7,
								parentId: 5,
								title: 'Inactive Page',
								url: '#',
								target: 'xxx',
								icon: 'far fa-circle',
								type: 'menu'
							}
						]
					}
				],
				// Sidebar的隐藏/显示动画效果是建立在display样式属性上的，
				// 因此必须事前设置该样式
				controlSidebarDisplay: 'none'
			}
		},
		beforeCreate() {
			// 阻止动画效果
			document.body.classList.add("hold-transition");
			// 最小化sidebar
			document.body.classList.add("sidebar-mini");
		},
		created() {
			// 完全模仿adminlte的作法，没搞明白这个样式为什么要先加上，而后再去掉
			document.body.classList.remove("hold-transition");
		},
		methods: {
			collapseSidebar() {
				if (!document.body.classList.contains("control-sidebar-slide-open")) {
					// 隐藏横向滚动条
					document.documentElement.classList.add("control-sidebar-animate");
					this.controlSidebarDisplay = 'block';
					setTimeout(function() {
						document.body.classList.add("control-sidebar-slide-open");
						setTimeout(function() {
							document.documentElement.classList.remove("control-sidebar-animate");
						},350)
					},10)
				} else {
					// 隐藏横向滚动条
					document.documentElement.classList.add("control-sidebar-animate");
					document.body.classList.remove("control-sidebar-slide-open");
					var that = this;
					setTimeout(function() {
						that.controlSidebarDisplay = 'none';
						document.documentElement.classList.remove("control-sidebar-animate");
					},350)
				}
			}
		}
	});
	
	app.component('lte-push-menu', PushMenu);
	app.component('lte-full-screen', FullScreent);
	app.component('lte-main-sidebar-logo', Logo);
	app.component('lte-copy-right', CopyRight);
	app.component('lte-version', Version);
	app.component('lte-control-sidebar', ControlSidebar);
	app.component('lte-main-sidebar', MainSidebar);
	
	app.component('lte-user-panel', {
		props: {
			title: {
				type: String,
				default: function() {
					return 'Alexander Pierce'
				}
			},
			logo: {
				type: String,
				default: function() {
					return 'img/user2-160x160.jpg'
				}
			}
		},
		template: `<div class="user-panel mt-3 pb-3 mb-3 d-flex">
		    <div class="image">
		      <img :src="logo" class="img-circle elevation-2" alt="User Image">
		    </div>
		    <div class="info">
		      <a href="#" class="d-block">{{title}}</a>
		    </div>
		  </div>`
	});
	
	app.component('lte-treeview', {
		props: {
			items: Array
		},
		data() {
			return {
				activeId: 2
			}
		},
		computed: {
			parentId() {
				for (var i = 0; i < this.items.length; i++) {
					var item = this.items[i];
					if (item.id == this.activeId) {
						return item.id;
					}
					if (item.children && item.children.length > 0) {
						for (var j = 0; j < item.children.length; j++) {
							var child = item.children[j];
							if (child.id == this.activeId) {
								return child.parentId;
							}
						}
					}
				}
				return null;
			}
		},
		methods: {
			menuSelect(event, id, url) {
				console.log(id, url, event);
				if (url && url.indexOf("http") === -1) {
					event.preventDefault();
					this.activeId = id;
				}
			}
		},
		template: `<ul class="nav nav-pills nav-sidebar nav-child-indent" data-widget="treeview" role="menu" data-accordion="false">
		      <template v-for="menu of items">
			  <lte-treeview-menu 
				:activeId="activeId" 
				:active="parentId == menu.id" 
				:opened="parentId == menu.id" 
				:menu="menu"
				@menu-select="menuSelect"></lte-treeview-menu>
			  </template>
		    </ul>`
	});
	
	app.component('lte-treeview-menu', {
		props: {
			menu: Object,
			active: {
				type: Boolean,
				default: function() {
					return false;
				}
			},
			activeId: Number | String,
			opened: {
				type: Boolean,
				default: function() {
					return false;
				}
			}
		},
		data() {
			return {
				// 菜单是否打开必须由此属性判断，否则视图更新时会丢失打开状态
				isOpen: this.opened
			}
		},
		methods: {
			toggle(event) {
				if (!this.menu.children || this.menu.children.length == 0) {
					return;
				}
				event.preventDefault();
				//console.log(this.$el.childNodes[1]); // li>ul
				const li = this.$el;
				var el = li.childNodes[1];
				
				// 动画时长（毫秒）
				const speed = 300;
				
				//console.log("isOpen", this.isOpen);
				// el.offsetHeight 是关键值，等于0时表示当前是关闭状态
				if (this.isOpen) {
					var that = this;
					const height = el.offsetHeight;
					animation({
						speed: speed,
						distance: height,
						before: function() {
							//console.log("collapse before")
							el.style.overflow = "hidden";
						},
						action: function(value) {
							//console.log("collapse action", value);
							el.style.height = (height - value) + "px";
						},
						after: function() {
							el.style.overflow = "";
							el.style.height = "";
							//console.log("collapse after");
							li.classList.remove("menu-open");
							that.isOpen = false;
						}
					})
				} else {
					this.isOpen = true;
					// 仅依靠 isOpen变更来更新视图会有一个延时，所以这里直接将样式加上
					li.classList.add("menu-open");
					// 必须设置overflow="hidden"才可以读到正确的offsetHeight
					el.style.overflow = "hidden";
					//height = el.offsetHeight;
					
					animation({
						speed: speed,
						distance: el.offsetHeight,
						before: function() {
							//console.log("expand before")
						},
						action: function(value) {
							//console.log("expand action", value);
							el.style.height = value + "px";
						},
						after: function() {
							el.style.overflow = "";
							el.style.height = "";
							//console.log("expand after");
						}
					})
				}
			}
		},
		template: `<li class="nav-item" :class="isOpen ? 'menu-open':''">
		        <a :href="menu.url" :target="menu.target" class="nav-link" :class="active ? 'active':''"
				 @click.stop="toggle">
		          <i class="nav-icon" :class="menu.icon"></i>
		          <p>
		            {{menu.title}}
					<span class="right badge badge-danger" v-if="menu.isNew && (!menu.children || menu.children.length == 0)">New</span>
		            <i class="right fas fa-angle-left" v-if="menu.children != null && menu.children.length > 0"></i>
		          </p>
		        </a>
		        <ul class="nav nav-treeview" v-if="menu.children != null && menu.children.length > 0">
		          <li class="nav-item" v-for="child of menu.children">
		            <a class="nav-link"
						:class="activeId == child.id ? 'active':''"
						:href="child.url"
						:target="child.target"
						@click="$emit('menuSelect',$event, child.id, child.url)">
		              <i class="nav-icon" :class="child.icon"></i>
		              <p>
						{{child.title}}
						<span class="right badge badge-danger" v-if="child.isNew">New</span>
					  </p>
		            </a>
		          </li>
		        </ul>
		      </li>`
	})
	
	app.component('lte-control-sidebar-button', {
		template: `<a class="nav-link" @click.prevent="$emit('toggleDisplay')" data-widget="control-sidebar" data-slide="true" href="#" role="button">
			  <i class="fas fa-th-large"></i>
			</a>`
	});
	
	app.component('lte-main-header', {
		template: `<nav class="main-header navbar navbar-expand navbar-white navbar-light" style="min-height:3.5rem;">
			<slot></slot>
		</nav>`
	});
	
	app.component('lte-main-header-navbar-left', {
		template: `<ul class="navbar-nav">
			  <!--d-none d-sm-inline-block 此组样式表示在窄屏（手机竖版）下隐藏-->
			  <li class="nav-item">
				<lte-push-menu/>
			  </li>
			</ul>`
	});
	
	app.component('lte-main-header-navbar-right', {
		methods: {
			onSidebarToggle() {
				// TODO 如何更好的传递到父组组件进行事件处理
				this.$emit('sidebarCollapse')
			}
		},
		template: `<ul class="navbar-nav ml-auto">
			  <li class="nav-item d-none d-sm-inline-block">
			    <lte-full-screen/>
			  </li>
			  <li class="nav-item">
			    <lte-control-sidebar-button @toggle-display="onSidebarToggle"/>
			  </li>
			</ul>`
	});
	
	app.component('lte-sidebar', {
		template: '<div class="sidebar"><slot><p style="color:white;">Menu</p></slot></div>'
	})
	
	app.component('lte-main-content', {
		template: `<div class="content-wrapper">
			<slot></slot>
		</div>`,
	});
	
	app.component('lte-main-footer', {
		template: `<footer class="main-footer" style="min-height:3.5rem;">
			<slot></slot>
		</footer>`,
	});
	
	// 挂载之前定义好组件，否则组件不生效
	const vm = app.mount('#app');
	console.log("it works!");
	
	function animation(option) {
		var speed = option.speed || 0; // 动画时长
		var distance = option.distance || 0; // 运动距离
		var before = option.before || empty; // 动画开始前
		var action = option.action || empty; // 动画执行中
		var after = option.after || empty; // 动画结束后
		
		var step = distance / (speed / 1000 * 60); // 步长
		var start;
		
		//console.log("step", step);
		if(step == 0) return;
		
		var w = window || {};
		if (!w.requestAnimationFrame) {
			w.requestAnimationFrame = w.webkitRequestAnimationFrame ||
			w.mozRequestAnimationFrame ||
			w.msRequestAnimationFrame ||
			w.oRequestAnimationFrame ||
			(function(cb) {setTimeout(cb, 16)});
		} 
		
		function empty() {}
				
		function doAction(timestamp) {
			if (start === undefined) {
				start = 0;
			}
			// 执行动画
			action(Math.min(start, distance));
			
			// 判断是否停止动画
			if (start <= distance) {
				//console.log("animation executing", start)
				w.requestAnimationFrame(doAction);
			} else {
				//console.log("animation execute after", start)
				after();
			}
			
			// 增加步长
			start += step;
		}
		
		before();	
		w.requestAnimationFrame(doAction);
	}
</script>
<style>
	/* 菜单选中时，父级菜单背景色渐显效果 */
	.sidebar-dark-primary .nav-sidebar>.nav-item>.nav-link.active, .sidebar-light-primary .nav-sidebar>.nav-item>.nav-link.active {
	    transition: background ease-in-out .3s, width ease-in-out .3s;
	}
</style>
</body>
</html>
